﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Interface;
using PServiceBus.Core.Runtime.Transports;
using System.Runtime.Serialization;
using System.ComponentModel.Composition;
using PServiceBus.Core.Runtime.Messages;
using PServiceBus.Core.Runtime.Extensions;
using System.Data.SqlClient;
using System.Data;

namespace PServiceBus.MSSQLTransport {
    [Serializable]
    [DataContract]
    [Export(typeof(ITransport))]
    public class MSSQLTransport : ITransport {

        static MSSQLTransport() {
            SerializationBootstrapper.Configure();
        }

        [DataMember]
        public string ConnectionString { get; set; }
        [DataMember]
        public MSSQLCommand Command { get; set; }
        [DataMember]
        public string DynamicQueryTemplate { get; set; }
        #region ITransport Members

        public TransportFormat Format { get; set; }

        [MessageShark.MessageSharkIgnore]
        private MessageMapper _messageMapper;

        public TransportResponse Send(string message, Dictionary<string, object> messageStructure) {
            var tranResponse = TransportResponse.New<MSSQLTransport>();
            try {
                _messageMapper = MessageMapper.Map(message, messageStructure);
                foreach (var msg in _messageMapper.Tokens) ExecuteCommand(msg);
                tranResponse.Success = true;
            } catch (Exception ex) {
                tranResponse.Message = ex.ToString();
            }
            return tranResponse;
        }

        private void ExecuteCommand(MessageMapperToken message) {
            using (var connection = new SqlConnection(ConnectionString)) {
                connection.Open();
                using (var transaction = connection.BeginTransaction()) {
                    using (var command = connection.CreateCommand()) {
                        command.Transaction = transaction;
                        ExecuteCommandByType(message, command);
                        ExecuteDynamicQuery(message, command);
                    }
                    transaction.Commit();
                }
            }
        }

        private void ExecuteDynamicQuery(MessageMapperToken message, SqlCommand command) {
            if (String.IsNullOrWhiteSpace(DynamicQueryTemplate)) return;
            command.CommandType = CommandType.Text;
            command.CommandText = message.TransformTemplate(DynamicQueryTemplate);
            command.ExecuteNonQuery();
        }

        private void ExecuteCommandByType(MessageMapperToken message, SqlCommand command) {
            if(Command == null) return;
            var mapping = Command.Mapping ?? new Dictionary<string, string>();
            var isCommandForStoreProcedure = Command.Type == MSSQLCommandType.InvokeStoreProcedure;
            var columns = new List<string>();
            var columnValues = new List<string>();
            if (!isCommandForStoreProcedure) {
                foreach (var param in mapping) {
                    columns.Add(param.Key);
                    columnValues.Add(message[param.Value]);
                }
            }
            command.CommandType = isCommandForStoreProcedure ? CommandType.StoredProcedure : CommandType.Text;
            command.CommandText = isCommandForStoreProcedure ? Command.CommandName : 
                String.Format("INSERT INTO {0}({1}) SELECT {2}",
                    Command.CommandName, String.Join(",", columns),
                    String.Join(",", columnValues.Select(value => String.Concat("'", value.Replace("'", "''"), "'"))));
            if (isCommandForStoreProcedure) 
                foreach (var param in mapping) 
                    command.Parameters.Add(new SqlParameter(String.Concat("@", param.Key.Replace("@","")), message[param.Value]));
            command.ExecuteNonQuery();
        }
        #endregion
    }
}
